package com.ymdd.library.permission;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import com.ymdd.library.permission.target.Target;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by 000286 on 2017/9/22.
 * Description
 */

public class DefaultRequest implements Request, Rationale, PermissionActivity.RationaleListener,
    PermissionActivity.PermissionListener {

  private static final String TAG = "MPermission";

  private Target mTarget;
  private int mRequestCode;
  private String[] mPermissions;
  private Object mCallback;
  private RationaleListener mRationaleListener;
  private String[] mDeniedPermissions;

  private Context mContext;

  public DefaultRequest(Target mTarget) {
    if (mTarget == null) {
      throw new IllegalArgumentException("The target can not be null.");
    }
    this.mTarget = mTarget;
  }

  @Override public void cancel() {
    int[] results = new int[mPermissions.length];
    for (int i = 0; i < mPermissions.length; i++) {
      results[i] = ContextCompat.checkSelfPermission(mTarget.getContext(), mPermissions[i]);
    }
    onRequestPermissionsResult(mPermissions, results);
  }

  @RequiresApi(api = Build.VERSION_CODES.M) @Override public void resume() {
    PermissionActivity.setPermissionListener(this);
    Intent mIntent = new Intent(mTarget.getContext(), PermissionActivity.class);
    mIntent.putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, mDeniedPermissions);
    mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    mTarget.startActivity(mIntent);
  }

  @NonNull @Override public Request permission(String... permissions) {
    this.mPermissions = permissions;
    return this;
  }

  @NonNull @Override public Request permission(String[]... permissionsArray) {
    List<String> permissionsList = new ArrayList<>();
    for (String[] permissions : permissionsArray) {
      for (String permission : permissions) {
        permissionsList.add(permission);
      }
    }
    this.mPermissions = permissionsList.toArray(new String[permissionsList.size()]);
    return this;
  }

  @NonNull @Override public Request rationale(RationaleListener mRationaleListener) {
    this.mRationaleListener = mRationaleListener;
    return this;
  }

  @NonNull @Override public Request requestCode(int requestCode) {
    this.mRequestCode = requestCode;
    return this;
  }

  @Override public Request callback(Object callback) {
    this.mCallback = callback;
    return this;
  }

  @Override public void start() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
      callbackSucceed();
    } else {
      mDeniedPermissions = getDeniedPermissions(mTarget.getContext(), mPermissions);
      if (mDeniedPermissions.length > 0) {
        PermissionActivity.setRationaleListener(this);
        Intent mIntent = new Intent(mTarget.getContext(), PermissionActivity.class);
        mIntent.putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, mDeniedPermissions);
        mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mTarget.startActivity(mIntent);
      } else {
        callbackSucceed();
      }
    }
  }

  private static String[] getDeniedPermissions(Context mContext, String[] mPermissions) {

    List<String> deniedList = new ArrayList<>(1);
    for (String permission : mPermissions) {
      if (ContextCompat.checkSelfPermission(mContext, permission)
          != PackageManager.PERMISSION_GRANTED) {
        deniedList.add(permission);
      }
    }
    return deniedList.toArray(new String[deniedList.size()]);
  }

  @Override public void onRationaleResult(boolean showRationale) {
    if (showRationale && mRationaleListener != null) {
      mRationaleListener.showRequestPermissionRationale(mRequestCode, this);
    } else {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        resume();
      }
    }
  }

  @Override public void onRequestPermissionsResult(@NonNull String[] permissions,
      @NonNull int[] grantResults) {
    List<String> deniedList = new ArrayList<>();
    //for (int i = 0; i < permissions.length; i++) {
    //  if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
    //    deniedList.add(permissions[i]);
    //  }
    //}

    for (int i = 0; i < permissions.length; i++) {
      if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {

        //适配android8.0 安装Apk权限
        if (permissions[i].equals(PermissionArray.UNKNOWNAPK[0])) {
          boolean b = mTarget.getContext().getPackageManager().canRequestPackageInstalls();
          if (!b) {
            deniedList.add(permissions[i]);
          }
        } else {
          deniedList.add(permissions[i]);
        }
      }
    }

    if (deniedList.isEmpty()) {
      callbackSucceed();
    } else {
      callbackFailed(deniedList);
    }
  }

  private void callbackSucceed() {
    if (mCallback != null) {
      if (mCallback instanceof PermissionListener) {
        ((PermissionListener) mCallback).onSuccess(mRequestCode, Arrays.asList(mPermissions));
      } else {
        callbackAnnotation(mCallback, mRequestCode, PermissionYes.class,
            Arrays.asList(mPermissions));
      }
    }
  }

  private void callbackFailed(List<String> deniedList) {
    if (mCallback != null) {
      if (mCallback instanceof PermissionListener) {
        ((PermissionListener) mCallback).onFailed(mRequestCode, deniedList);
      } else {
        callbackAnnotation(mCallback, mRequestCode, PermissionNo.class, deniedList);
      }
    }
  }

  private void callbackAnnotation(Object mCallback, int mRequestCode,
      Class<? extends Annotation> annotationClass, List<String> permissions) {
    Method[] methods =
        findMethodForRequestCode(mCallback.getClass(), annotationClass, mRequestCode);

    if (methods.length == 0) {
      Log.e(TAG, "Do you forget @PermissionYes or @PermissionNo for callback method ?");
    } else {
      try {
        for (Method method : methods) {
          if (!method.isAccessible()) {
            method.setAccessible(true);
          }
          method.invoke(mCallback, permissions);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  private Method[] findMethodForRequestCode(Class<?> source, Class<? extends Annotation> annotation,
      int mRequestCode) {

    List<Method> methods = new ArrayList<>(1);
    for (Method method : source.getDeclaredMethods()) {
      if (method.getAnnotation(annotation) != null) {
        if (isSameRequestCode(method, annotation, mRequestCode)) {
          methods.add(method);
        }
      }
    }
    return methods.toArray(new Method[methods.size()]);
  }

  private boolean isSameRequestCode(Method mMethod, Class<? extends Annotation> mAnnotation,
      int mRequestCode) {
    if (PermissionYes.class.equals(mAnnotation)) {
      return mMethod.getAnnotation(PermissionYes.class).value() == mRequestCode;
    } else if (PermissionNo.class.equals(mAnnotation)) {
      return mMethod.getAnnotation(PermissionNo.class).value() == mRequestCode;
    } else {
      return false;
    }
  }
}
